<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script>

//
// INK EQUATIONS
//

// Animation constants.
var globalSpeed = 1;
var waveOpacityDecayVelocity = 0.8 / globalSpeed;  // opacity per second.
var waveInitialOpacity = 0.25;
var waveLingerOnTouchUp = 0.2;
var waveMaxRadius = 150; 

// TODOs:
// - rather than max distance to corner, use hypotenuos(sp) (diag)
// - use quadratic for the fall off, move fast at the beginning, 
// - on cancel, immediately fade out, reverse the direction

function waveRadiusFn(touchDownMs, touchUpMs, ww, hh) {
  // Convert from ms to s.
  var touchDown = touchDownMs / 1000;
  var touchUp = touchUpMs / 1000;
  var totalElapsed = touchDown + touchUp;
  var waveRadius = Math.min(Math.max(ww, hh), waveMaxRadius) * 1.1 + 5;
  var dduration = 1.1 - .2 * (waveRadius / waveMaxRadius);
  var tt = (totalElapsed / dduration);
  
  var ssize = waveRadius * (1 - Math.pow(80, -tt));
  return Math.abs(ssize);
}

function waveOpacityFn(td, tu) {
  // Convert from ms to s.
  var touchDown = td / 1000;
  var touchUp = tu / 1000;
  var totalElapsed = touchDown + touchUp;

  if (tu <= 0) {  // before touch up
    return waveInitialOpacity;
  }
  return Math.max(0, waveInitialOpacity - touchUp * waveOpacityDecayVelocity);
}

function waveOuterOpacityFn(td, tu) {
  // Convert from ms to s.
  var touchDown = td / 1000;
  var touchUp = tu / 1000;

  // Linear increase in background opacity, capped at the opacity
  // of the wavefront (waveOpacity).
  var outerOpacity = touchDown * 0.3;
  var waveOpacity = waveOpacityFn(td, tu);
  return Math.max(0, Math.min(outerOpacity, waveOpacity));
  
}

function waveGravityToCenterPercentageFn(td, tu, r) {
  // Convert from ms to s.
  var touchDown = td / 1000;
  var touchUp = tu / 1000;
  var totalElapsed = touchDown + touchUp;

  return Math.min(1.0, touchUp * 6);
}


// Determines whether the wave should be completely removed.
function waveDidFinish(wave, radius) {
  var waveOpacity = waveOpacityFn(wave.tDown, wave.tUp);
  // Does not linger any more.
  // var lingerTimeMs = waveLingerOnTouchUp * 1000;

  // If the wave opacity is 0 and the radius exceeds the bounds
  // of the element, then this is finished.
  if (waveOpacity < 0.01 && radius >= wave.maxRadius) {
    return true;
  }
  return false;
};

//
// DRAWING
//

function animateIcon() {
  var el = document.getElementById('button_toolbar0');
  el.classList.add('animate');
  setTimeout(function(){
    el.classList.remove('animate');
    el.classList.toggle('selected');
  }, 500);
}


function drawRipple(canvas, x, y, radius, innerColor, outerColor, innerColorAlpha, outerColorAlpha) {
  var ctx = canvas.getContext('2d');
  if (outerColor) {
    ctx.fillStyle = outerColor;
    ctx.fillRect(0,0,canvas.width, canvas.height);
  }

  ctx.beginPath();
  ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
  ctx.fillStyle = innerColor;
  ctx.fill();
}

function drawLabel(canvas, label, fontSize, color, alignment) {
  var ctx = canvas.getContext('2d');
  ctx.font= fontSize + 'px Helvetica';

  var metrics = ctx.measureText(label);
  var width = metrics.width;
  var height = metrics.height;
  ctx.fillStyle = color;

  var xPos = (canvas.width/2 - width)/2;

  if (alignment === 'left') { xPos = 16; }

  ctx.fillText(label, xPos, canvas.height/2 - (canvas.height/2 - fontSize +2) / 2);
}

//
// BUTTON SETUP
//

function createWave(elem) {
  var elementStyle = window.getComputedStyle(elem);
  var fgColor = elementStyle.color;

  var wave = {
    waveColor: fgColor,
    maxRadius: 0,
    isMouseDown: false,
    mouseDownStart: 0.0,
    mouseUpStart: 0.0,
    tDown: 0,
    tUp: 0
  };
  return wave;
}

function removeWaveFromScope(scope, wave) {
  if (scope.waves) {
    var pos = scope.waves.indexOf(wave);
    scope.waves.splice(pos, 1);
  }
};


function setUpPaperByClass( classname ) {
  var elems = document.querySelectorAll( classname );
  [].forEach.call( elems, function( el ) {
      setUpPaper(el);
  });
}

function setUpPaper(elem) {
  var pixelDensity = 2;

  var elementStyle = window.getComputedStyle(elem);
  var fgColor = elementStyle.color;
  var bgColor = elementStyle.backgroundColor;
  elem.width = elem.clientWidth;
  elem.setAttribute('width', elem.clientWidth * pixelDensity + "px");
  elem.setAttribute('height', elem.clientHeight * pixelDensity + "px");

  var isButton = elem.classList.contains( 'button' ) || elem.classList.contains( 'button_floating' ) | elem.classList.contains( 'button_menu' );
  var isToolbarButton =  elem.classList.contains( 'button_toolbar' );

  elem.getContext('2d').scale(pixelDensity, pixelDensity)

  var scope = {
    backgroundFill: true,
    element: elem,
    label: 'Button',
    waves: [],
  };


  scope.label = elem.getAttribute('value') || elementStyle.content;
  scope.labelFontSize = elementStyle.fontSize.split("px")[0];

  drawLabel(elem, scope.label, scope.labelFontSize, fgColor, elem.style.textAlign);


  //
  // RENDER FOR EACH FRAME
  //
  var onFrame = function() {
    var shouldRenderNextFrame = false;

    // Clear the canvas
    var ctx = elem.getContext('2d');
    ctx.clearRect(0, 0, elem.width, elem.height);

    var deleteTheseWaves = [];
    // The oldest wave's touch down duration
    var longestTouchDownDuration = 0;
    var longestTouchUpDuration = 0;
    // Save the last known wave color
    var lastWaveColor = null;

    for (var i = 0; i < scope.waves.length; i++) {
      var wave = scope.waves[i];

      if (wave.mouseDownStart > 0) {
        wave.tDown = now() - wave.mouseDownStart;
      }
      if (wave.mouseUpStart > 0) {
        wave.tUp = now() - wave.mouseUpStart;
      }

      // Determine how long the touch has been up or down.
      var tUp = wave.tUp;
      var tDown = wave.tDown;
      longestTouchDownDuration = Math.max(longestTouchDownDuration, tDown);
      longestTouchUpDuration = Math.max(longestTouchUpDuration, tUp);

      // Obtain the instantenous size and alpha of the ripple.
      var radius = waveRadiusFn(tDown, tUp, elem.width, elem.height);
      var waveAlpha =  waveOpacityFn(tDown, tUp);
      var waveColor = cssColorWithAlpha(wave.waveColor, waveAlpha);
      lastWaveColor = wave.waveColor;

      // Position of the ripple.
      var x = wave.startPosition.x;
      var y = wave.startPosition.y;

      // Ripple gravitational pull to the center of the canvas.
      if (wave.endPosition) {
 
        var translateFraction = waveGravityToCenterPercentageFn(tDown, tUp, wave.maxRadius);

        // This translates from the origin to the center of the view  based on the max dimension of  
        var translateFraction = Math.min(1, radius / wave.containerSize * 2 / Math.sqrt(2) );

        x += translateFraction * (wave.endPosition.x - wave.startPosition.x);
        y += translateFraction * (wave.endPosition.y - wave.startPosition.y);
      }

      // If we do a background fill fade too, work out the correct color.
      var bgFillColor = null;
      if (scope.backgroundFill) {
        var bgFillAlpha = waveOuterOpacityFn(tDown, tUp);
        bgFillColor = cssColorWithAlpha(wave.waveColor, bgFillAlpha);
      }

      // Draw the ripple.
      drawRipple(elem, x, y, radius, waveColor, bgFillColor);

      // Determine whether there is any more rendering to be done.
      var shouldRenderWaveAgain = !waveDidFinish(wave, radius);
      shouldRenderNextFrame = shouldRenderNextFrame || shouldRenderWaveAgain;
      if (!shouldRenderWaveAgain) {
        deleteTheseWaves.push(wave);
      }
   }

    if (shouldRenderNextFrame) {
      window.requestAnimationFrame(onFrame);
    }  else {
      // If there is nothing to draw, clear any drawn waves now because
      // we're not going to get another requestAnimationFrame any more.
      var ctx = elem.getContext('2d');
      ctx.clearRect(0, 0, elem.width, elem.height);
    }

    // Draw the label at the very last point so it is on top of everything.
    drawLabel(elem, scope.label, scope.labelFontSize, fgColor, elem.style.textAlign);

    for (var i = 0; i < deleteTheseWaves.length; ++i) {
      var wave = deleteTheseWaves[i];
      removeWaveFromScope(scope, wave);
    }
  };

  //
  // MOUSE DOWN HANDLER
  //

  elem.addEventListener('mousedown', function(e) {
    var wave = createWave(e.target);
    var elem = scope.element;

    wave.isMouseDown = true;
    wave.tDown = 0.0;
    wave.tUp = 0.0;
    wave.mouseUpStart = 0.0;
    wave.mouseDownStart = now();

    var width = e.target.width / 2; // Retina canvas
    var height = e.target.height / 2;
    var touchX = e.clientX - e.target.offsetLeft - e.target.offsetParent.offsetLeft;
    var touchY = e.clientY - e.target.offsetTop - e.target.offsetParent.offsetTop;
    wave.startPosition = {x:touchX, y:touchY};

    if (elem.classList.contains("recenteringTouch")) {
      wave.endPosition = {x: width / 2,  y: height / 2};
      wave.slideDistance = dist(wave.startPosition, wave.endPosition);
    }
    wave.containerSize = Math.max(width, height);
    wave.maxRadius = distanceFromPointToFurthestCorner(wave.startPosition, {w: width, h: height});
    elem.classList.add("activated");
    scope.waves.push(wave);
    window.requestAnimationFrame(onFrame);
    return false;
  });

  //
  // MOUSE UP HANDLER
  //

  elem.addEventListener('mouseup', function(e) {
    elem.classList.remove("activated");

    for (var i = 0; i < scope.waves.length; i++) {
      // Declare the next wave that has mouse down to be mouse'ed up.
      var wave = scope.waves[i];
      if (wave.isMouseDown) {
        wave.isMouseDown = false
        wave.mouseUpStart = now();
        wave.mouseDownStart = 0;
        wave.tUp = 0.0;
        break;
      }
    }
    return false;
  });

  elem.addEventListener('mouseout', function(e) {
  elem.classList.remove("activated");

  for (var i = 0; i < scope.waves.length; i++) {
    // Declare the next wave that has mouse down to be mouse'ed up.
    var wave = scope.waves[i];
    if (wave.isMouseDown) {
      wave.isMouseDown = false
      wave.mouseUpStart = now();
      wave.mouseDownStart = 0;
      wave.tUp = 0.0;
      wave.cancelled = true;
      break;
    }
  }
  return false;
  });

  return scope;
};

// Shortcuts.
var pow = Math.pow;
var now = function() { return new Date().getTime(); };

// Quad beizer where t is between 0 and 1.
function quadBezier(t, p0, p1, p2, p3) {
  return pow(1 - t, 3) * p0 +
         3 * pow(1 - t, 2) * t * p1 +
         (1 - t) * pow(t, 2) * p2 +
         pow(t, 3) * p3;
}

function easeIn(t) {
  return quadBezier(t, 0.4, 0.0, 1, 1);
}

function cssColorWithAlpha(cssColor, alpha) {
    var parts = cssColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
    if (typeof alpha == 'undefined') {
        alpha = 1;
    }
    if (!parts) {
      return 'rgba(255, 255, 255, ' + alpha + ')';
    }
    return 'rgba(' + parts[1] + ', ' + parts[2] + ', ' + parts[3] + ', ' + alpha + ')';
}

function dist(p1, p2) {
  return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}

function distanceFromPointToFurthestCorner(point, size) {
  var tl_d = dist(point, {x: 0, y: 0});
  var tr_d = dist(point, {x: size.w, y: 0});
  var bl_d = dist(point, {x: 0, y: size.h});
  var br_d = dist(point, {x: size.w, y: size.h});
  return Math.max(Math.max(tl_d, tr_d), Math.max(bl_d, br_d));
}


function toggleDialog() {
  var el = document.getElementById('dialog');
  el.classList.toggle("visible");
}

function toggleMenu() {
  var el = document.getElementById('menu');
  el.classList.toggle("visible");
}


// Initialize

function init() {
    setUpPaperByClass( '.paper' );
}

window.addEventListener('DOMContentLoaded', init, false);
</script>

<style type="text/css" media="screen">
  body {
  background-color: #f9f9f9;
    margin:0; padding:0;
    font-family:sans-serif;
    font-size:14px;
}

* {

    -webkit-user-select: none;
    cursor:default;
}
.toolbar {
    background-color:#673AB7;
    height:56px;
    overflow:hidden;
}


#dialog {
  width:250px;
  height:120px;
  opacity:0.0;
  pointer-events:none;
  -webkit-transform: scale(0.95) translate3d(0,100px,0);;
  -webkit-transition: -webkit-transform 0.3s ease-out, height 0.2s ease-out, opacity 0.2s ease-out, -webkit-box-shadow .5s ease-out;

    -webkit-box-shadow:0 10px 10px rgba(0,0,0,0.12), 0 5px 5px rgba(0,0,0,0.24);
      -webkit-transition-delay:0.25s;

}

#dialog.visible {
  pointer-events:all;
  opacity:1.0;
  -webkit-transform: scale(1.0) translate3d(0,0,0);;
  width:250px;
  height:160px;
      -webkit-box-shadow:0 19px 19px rgba(0,0,0,0.30), 0 15px 6px rgba(0,0,0,0.22);
  -webkit-transition-delay:0.0s;

}


.dialog {
    top:120px;
    right: 0;
    left: 50%;
    margin-left:-125px;
    padding:24px 16px 20px 24px;
    position:absolute;

    background:white;
    color:rgba(0,0,0,0.5);
    margin-bottom:38px;
    overflow:hidden;
}
.dialog h1 {
    font-size:20px;
    font-weight:normal;
    padding:0; margin-top:0;
}

.dialog .button {
    position:absolute;
    top:160px;
    right:10px;
}

/*#menu {
  width:250px;
  height:120px;
  opacity:0.0;
  pointer-events:none;
  -webkit-transform: scale(0.95) translate3d(0,100px,0);;
  -webkit-transition: -webkit-transform 0.3s ease-out, height 0.2s ease-out, opacity 0.2s ease-out, -webkit-box-shadow .5s ease-out;

    -webkit-box-shadow:0 10px 10px rgba(0,0,0,0.12), 0 5px 5px rgba(0,0,0,0.24);
}

#menu.visible {
  pointer-events:all;
  opacity:1.0;
  -webkit-transform: scale(1.0) translate3d(0,0,0);;
  width:250px;
  height:160px;
      -webkit-box-shadow:0 19px 19px rgba(0,0,0,0.30), 0 15px 6px rgba(0,0,0,0.22);
}*/




.menu {
    width:160px;
    height:160px;
    background:white;
    position:absolute;
    right:8px;
    top: 8px;
    padding:0;


  opacity:0.0;
  pointer-events:none;
  -webkit-transform: scale(0.95) translate3d(0,0,0);;
  -webkit-transition: -webkit-transform 0.3s ease-out, width 0.2s ease-out, height 0.3s ease-out, opacity 0.1s ease-out, -webkit-box-shadow .5s ease-out;

    -webkit-box-shadow:0 10px 10px rgba(0,0,0,0.12), 0 5px 5px rgba(0,0,0,0.24);
  -webkit-transition-delay:0.35s;
  overflow:hidden;


}

.menu.visible {
  width:180px;
  height:192px;
  pointer-events:all;
  opacity:1.0;
  -webkit-transform: scale(1.0) translate3d(0,0,0);;
  -webkit-box-shadow:0 10px 10px rgba(0,0,0,0.12), 0 5px 5px rgba(0,0,0,0.24);
    -webkit-transition-delay:0.15s;

}



.menu div {
    height:48px;
    padding:0;

}

.menu canvas {
    height:48px;
    width: 100%;
}


.button_floating {
    width:56px;
    height:56px;
    color:#fff;
    background-color:#039BE5;
    border-radius:28px;
    position:absolute;
    top:388px;
    right:40px;
}

.container {
}




.button {
  -webkit-border-radius: 2px;
  -moz-border-radius: 2px;
  border-radius: 2px;
}

.button_menu.container {
    margin: 0px;
    height: 48px;
    padding: 0;
}



.button_menu {
  -webkit-border-radius: 2px;
  -moz-border-radius: 2px;
  border-radius: 2px;
    text-align:left !important;
    color:rgb(100,100,100);
}

.button_toolbar {
    -webkit-border-radius: 84px;

    color: rgb(255,255,255);
    width: 80px;
    height: 80px;
    float:left;
    margin-right:-24px;
    margin-top:-12px;
    background-color: rgba(0, 0, 0, 0);
}

#button_small {
   color: rgb(100, 100, 100);
    background-color: #ffffff;
    width: 80px;
    height: 32px;

}

#button_blue {
   color: rgb(255, 255, 255);
    background-color: #0277BD;
    width: 80px;
    height: 32px;
}

#button_borderless_square {
  color: #ffffff;
  background-color: #f9f9f9;
  border: 1px solid #f0f0f0;
  width: 100px;
  height: 100px;
  background-color: rgb(255, 255, 255, 0);
  background-image:url(http://wallpaperandbackground.com/wp-content/uploads/2014/03/Field-Landscape.jpg);
    background-size:cover;

}

#button_borderless {
  color: #777777;
  background-color: #ffffff;
  width:80px;
  height: 32px;
    margin-right:72px;
    content: "CANCEL";
}

#button_borderless_blue {
  color: #4285F4;
  background-color: #ffffff;
  width: 64px;
  height: 32px;
  content:"OK";
}

#button_large {
  color: rgb(255, 255, 255);
  width: 100%;
  height: 360px;
  background-color:#81D4FA;
}

#button_toolbar1 {
    content: '';
    color:transparent;
        margin-left:-4px;

}

#button_toolbar2 {
  content: '★';
  float:right;
}

#button_toolbar3 {
    content: 'MENU';
    font-size: 14px;
    width: 88px;
    height:88px;
    margin-top: -16px;
    color:#7FFFFF;
    float:right;
    margin-right:0;

}

.caption {
  z-index: 100;
  background-color: red;
}

#button_toolbar0.animate {
    -webkit-animation: play 0.5s steps(16);

}


.button.raised {
  -webkit-transition: -webkit-box-shadow 0.2s;
  -webkit-transition-delay: 0.2s;
  -webkit-box-shadow:0 1px 2px rgba(0,0,0,0.12), 0 1px 1px rgba(0,0,0,0.24);
}

.button.raised.activated {
  -webkit-box-shadow: 0px 10px 10px rgba(0,0,0,0.19), 0px 6px 3px rgba(0,0,0,0.23);
  -webkit-transition-delay: 0.0s;
}


.floating {
  -webkit-transition: -webkit-box-shadow 0.2s;
  -webkit-transition-delay: 0.2s;
  -webkit-box-shadow:0 3px 3px rgba(0,0,0,0.16), 0 3px 3px rgba(0,0,0,0.23);
}

.floating.activated {
  -webkit-box-shadow: 0px 14px 14px rgba(0,0,0,0.25), 0px 10px 5px rgba(0,0,0,0.22);
  -webkit-transition-delay: 0.0s;
}

#title {
  color:white;
  font-size:20px;
  line-height:58px;
  margin-left:24px;
}

.content {
  padding-left:72px; padding-top:24px; padding-right:144px;
  line-height:20px;
  color:#666;
}

@-webkit-keyframes play {
   from { background-position: 0px 0px; }
     to { background-position: 0px -384px; }
}

#button_toolbar0 {
  pointer-events: none;
  position:absolute;
  top:16px;
  left: 24px;
  content: "\00a0";
    width:24px;
    height:24px;
  background-image: url();
}

#button_toolbar0.selected {
  background-image: url();
}
</style>
</head>

<body id="">
  <div class="toolbar">
    <div id="button_toolbar0" class="toolbar_icon"></div>
    <span class="container"><canvas class="paper button_toolbar recenteringTouch" id="button_toolbar1" onclick="animateIcon()"></canvas></span>
    <span id="title" style="float:left">Ripple test</span>
    <span class="container"><canvas onClick="toggleMenu()" class="paper button_toolbar recenteringTouch" id="button_toolbar3"></canvas></span>
    <span class="container"><canvas class="paper button_toolbar recenteringTouch" id="button_toolbar2"></canvas></span>
    <span class="container"><canvas class="paper button_toolbar recenteringTouch" id="button_toolbar2"></canvas></span>

</div>

<div id="dialog" class="dialog visible">
    <h1>Press mah buttons!</h1> Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam. 
    <span class="container"><canvas class="paper button" id="button_borderless" value="CANCEL"></canvas></span>
    <span class="container"><canvas class="paper button" id="button_borderless_blue" onclick="toggleDialog()" value="OK"></canvas></span>
</div>

<div id="menu" class="menu visible">
    <div class="container button_menu"><canvas class="paper button_menu" value="Fold" style="text-align:left"></canvas></div>
    <div class="container button_menu"><canvas class="paper button_menu" value="Spindle" style="text-align:left"></canvas></div>
    <div class="container button_menu"><canvas class="paper button_menu" onClick="" value="Mutilate" style="text-align:left"></canvas></div>
        <div class="container button_menu"><canvas class="paper button_menu" onClick="toggleMenu()" value="Dismiss" style="text-align:left"></canvas></div>

</div>
<div class="container"><canvas class="paper button recenteringTouch" id="button_large"></canvas></div>
<div class="content" style="">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam. sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
<p></p>
<span class="container" style="margin-right:8px;"><canvas class="paper button raised" id="button_blue" value="ABORT"></canvas></span>
<span class="container" style="margin-right:8px;"><canvas class="paper button raised" id="button_small" value="RETRY"></canvas></span>
  <span class="container"><canvas class="paper button raised" id="button_small" value="FAIL"></canvas></span>

  <p><p>
<canvas class="paper button" id="button_borderless_square"></canvas></div>


</div>
<div class="container"><canvas onClick="toggleDialog()" class="paper button_floating floating recenteringTouch" id="button_floating" value="★"></canvas></div>
</body>
</html>
